#ifndef __LDX_ISCENENODE_H_INCLUDE__
#define __LDX_ISCENENODE_H_INCLUDE__
#include "LDXConfig.h"
#include "LDXConfig.h"
#include "IReferenceCounted.h"
#include "SMaterial.h"
#include "ESceneNodeTypes.h"
#include "ISceneNodeAnimator.h"
namespace ldx
{
	namespace scene
	{
		class CSceneManager;
		//class ISceneNodeAnimator;
		class ISceneNode;
		//! Typedef for list of scene nodes
		typedef std::list<ISceneNode*> ISceneNodeList;
		//! Typedef for list of scene node animators
		typedef std::list<ISceneNodeAnimator*> ISceneNodeAnimatorList;

		class ISceneNode :public IReferenceCounted
		{
		public:

			//! Constructor
			ISceneNode(ISceneNode* parent, CSceneManager* mgr, int id = -1,
				const mt::vec3f& position = mt::vec3f(0, 0, 0),
				const mt::vec3f& rotation = mt::vec3f(0, 0, 0),
				const mt::vec3f& scale = mt::vec3f(1.0f, 1.0f, 1.0f))
				: RelativeTranslation(position), RelativeRotation(rotation), RelativeScale(scale),
				Parent(0), SceneManager(mgr), ID(id),
				IsVisible(true), NodeType(ESNT_UNKNOWN)
			{
				if (parent)
					parent->addChild(this);

				updateAbsolutePosition();
			}


			//! Destructor
			virtual ~ISceneNode()
			{
				// delete all children
				removeAll();
				// delete all animators
				std::list<ISceneNodeAnimator*>::iterator ait = Animators.begin();
				for (; ait != Animators.end(); ++ait)
					(*ait)->drop();
			}

			virtual void OnRegisterSceneNode()
			{
				if (IsVisible)
				{
					std::list<ISceneNode*>::iterator it = Children.begin();
					for (; it != Children.end(); ++it)
						(*it)->OnRegisterSceneNode();
				}
			}

			//! OnAnimate() is called just before rendering the whole scene.
			/** Nodes may calculate or store animations here, and may do other useful things,
			depending on what they are. Also, OnAnimate() should be called for all
			child scene nodes here. This method will be called once per frame, independent
			of whether the scene node is visible or not.
			\param timeMs Current time in milliseconds. */
			virtual void OnAnimate(float timeMs)
			{
				if (IsVisible)
				{
					// animate this node with all animators

					std::list<ISceneNodeAnimator*>::iterator ait = Animators.begin();
					while (ait != Animators.end())
					{
						// continue to the next node before calling animateNode()
						// so that the animator may remove itself from the scene
						// node without the iterator becoming invalid
						ISceneNodeAnimator* anim = *ait;
						++ait;
						anim->animateNode(this, timeMs);
					}

					// update absolute position
					updateAbsolutePosition();

					// perform the post render process on all children

					std::list<ISceneNode*>::iterator it = Children.begin();
					for (; it != Children.end(); ++it)
						(*it)->OnAnimate(timeMs);
				}
			}

			//! Renders the node.
			virtual void render()
			{

			}

			virtual void update(float deltaTime)
			{
				updateAbsolutePosition();
			}


			//! Returns the name of the node.
			/** \return Name as character string. */
			virtual const char* getName() const
			{
				return Name.c_str();
			}


			//! Sets the name of the node.
			/** \param name New name of the scene node. */
			virtual void setName(const char* name)
			{
				Name = name;
			}

			//! Sets the name of the node.
			/** \param name New name of the scene node. */
			virtual void setName(const std::string& name)
			{
				Name = name;
			}

			//! Get the absolute transformation of the node. Is recalculated every OnAnimate()-call.
			virtual const mt::mat4& getAbsoluteTransformation() const
			{
				return AbsoluteTransformation;
			}

			//! Returns the relative transformation of the scene node.
			virtual mt::mat4 getRelativeTransformation() const
			{
				//scale,rotate,translate
				mt::mat4 mat(1.0f);
				mt::mat4 mrotate(1.0f);
				mt::mat4 mtranslate(1.0f);
				if (RelativeScale != mt::vec3f(1.f, 1.f, 1.f))
				{					
					mat.setScale(RelativeScale);
				}
				mrotate.setRotationDegrees(RelativeRotation);
				mat *= mrotate;
				mat.setTranslation(RelativeTranslation);
				//mat *= mtranslate;
				return mat;
			}

			virtual bool isVisible() const
			{
				return IsVisible;
			}

			virtual bool isTrulyVisible() const
			{
				if (!IsVisible)
					return false;

				if (!Parent)
					return true;

				return Parent->isTrulyVisible();
			}


			virtual void setVisible(bool isVisible)
			{
				IsVisible = isVisible;
			}

			virtual int getID() const
			{
				return ID;
			}


			//! Sets the id of the scene node.
			virtual void setID(int id)
			{
				ID = id;
			}

			virtual void addChild(ISceneNode* child)
			{
				if (child && (child != this))
				{
					// Change scene manager?
					if (SceneManager != child->SceneManager)
						child->setSceneManager(SceneManager);

					child->grab();
					child->remove(); // remove from old parent
					Children.push_back(child);
					child->Parent = this;
				}
			}

			//! Removes a child from this scene node.
			virtual bool removeChild(ISceneNode* child)
			{
				std::list<ISceneNode*>::iterator it = Children.begin();
				for (; it != Children.end(); ++it)
					if ((*it) == child)
					{

						(*it)->Parent = 0;
						(*it)->drop();
						Children.erase(it);
						return true;
					}

				return false;
			}

			//! Removes all children of this scene node
			/** The scene nodes found in the children list are also dropped
			and might be deleted if no other grab exists on them.
			*/
			virtual void removeAll()
			{
				std::list<ISceneNode*>::iterator it = Children.begin();
				for (; it != Children.end(); ++it)
				{
					(*it)->Parent = 0;
					(*it)->drop();
				}

				Children.clear();
			}


			//! Removes this scene node from the scene
			/** If no other grab exists for this node, it will be deleted.
			*/

			virtual void remove()
			{
				if (Parent)
					Parent->removeChild(this);
			}


			//! Adds an animator which should animate this node.
			/** \param animator A pointer to the new animator. */
			virtual void addAnimator(ISceneNodeAnimator* animator)
			{
				if (animator)
				{
					Animators.push_back(animator);
					animator->grab();
				}
			}


			//! Get a list of all scene node animators.
			/** \return The list of animators attached to this node. */
			const std::list<ISceneNodeAnimator*>& getAnimators() const
			{
				return Animators;
			}


			//! Removes an animator from this scene node.
			/** If the animator is found, it is also dropped and might be
			deleted if not other grab exists for it.
			\param animator A pointer to the animator to be deleted. */
			virtual void removeAnimator(ISceneNodeAnimator* animator)
			{
				std::list<ISceneNodeAnimator*>::iterator it = Animators.begin();
				for (; it != Animators.end(); ++it)
				{
					if ((*it) == animator)
					{
						(*it)->drop();
						Animators.erase(it);
						return;
					}
				}
			}

			//! Removes all animators from this scene node.
			/** The animators might also be deleted if no other grab exists
			for them. */
			virtual void removeAnimators()
			{
				std::list<ISceneNodeAnimator*>::iterator it = Animators.begin();
				for (; it != Animators.end(); ++it)
					(*it)->drop();

				Animators.clear();
			}



			virtual uint getMaterialCount() const
			{
				return 0;
			}


			virtual render::SMaterial& getMaterial(uint num)
			{
				return render::IdentityMaterial;
			}

			//! Sets the material type of all materials in this scene node to a new material type.
			/** \param newType New type of material to be set. */
			void setMaterialType(render::E_MATERIAL_TYPE newType)
			{
				for (uint i = 0; i<getMaterialCount(); ++i)
					getMaterial(i).MaterialType = newType;
			}

			//! Set the new material for one scene node
			// If custom the program,material.ProgramID should be set.
			virtual void setMaterial(render::SMaterial material)
			{

			}

			//relative
			virtual const mt::vec3f& getScale() const
			{
				return RelativeScale;
			}

			//! Sets the relative scale of the scene node.
			// If has parent,will influent translation.
			virtual void setScale(const mt::vec3f& scale)
			{
				RelativeScale = scale;
			}

			//! Gets the rotation of the node relative to its parent.
			virtual const mt::vec3f& getRotation() const
			{
				return RelativeRotation;
			}

			//! Sets the rotation of the node relative to its parent.
			/** This only modifies the relative rotation of the node.
			\param rotation New rotation of the node in degrees. */
			virtual void setRotation(const mt::vec3f& rotation)
			{
				RelativeRotation = rotation;
			}

			//! Sets the position of the node relative to its parent.
			/** Note that the position is relative to the parent.
			\param newpos New relative position of the scene node. */
			virtual void setPosition(const mt::vec3f& newpos)
			{
				RelativeTranslation = newpos;
			}

			//! Gets the position of the node relative to its parent.
			virtual const mt::vec3f& getPosition() const
			{
				return RelativeTranslation;
			}

			//! Gets the absolute position of the node in world coordinates.
			virtual mt::vec3f getAbsolutePosition() const
			{
				return AbsoluteTransformation.getTranslation();
			}

			//! Returns a const reference to the list of all children.
			/** \return The list of all children of this node. */
			const std::list<ISceneNode*>& getChildren() const
			{
				return Children;
			}

			virtual void setParent(ISceneNode* newParent)
			{
				grab();
				remove();

				Parent = newParent;

				if (Parent)
					Parent->addChild(this);

				drop();
			}

			//! Updates the absolute position based on the relative and the parents position
			/** Note: This does not recursively update the parents absolute positions, so if you have a deeper
			hierarchy you might want to update the parents first.*/
			virtual void updateAbsolutePosition()
			{
				if (Parent)
				{
					AbsoluteTransformation =
						Parent->getAbsoluteTransformation() * getRelativeTransformation();
				}
				else
					AbsoluteTransformation = getRelativeTransformation();
			}

			//! Returns the parent of this scene node
			/** \return A pointer to the parent. */
			scene::ISceneNode* getParent() const
			{
				return Parent;
			}

			//! Returns type of the scene node
			/** \return The type of this node. */
			virtual ESCENE_NODE_TYPE getType() const
			{
				return NodeType;
			}

			virtual void setNodeType(ESCENE_NODE_TYPE ent)
			{
				NodeType = ent;
			}

			virtual CSceneManager* getSceneManager(void)
			{
				return SceneManager;
			}

			//! Sets the new scene manager for this node and all children.
			void setSceneManager(CSceneManager* newManager)
			{
				SceneManager = newManager;

				std::list<ISceneNode*>::iterator it = Children.begin();
				for (; it != Children.end(); ++it)
					(*it)->setSceneManager(newManager);
			}

		protected:

			ESCENE_NODE_TYPE NodeType;

			//! Name of the scene node.
			std::string Name;

			//! Absolute transformation of the node.
			mt::mat4 AbsoluteTransformation;

			//! Relative translation of the scene node.
			mt::vec3f RelativeTranslation;

			//! Relative rotation of the scene node.
			mt::vec3f RelativeRotation;

			//! Relative scale of the scene node.
			mt::vec3f RelativeScale;

			//! Pointer to the parent
			ISceneNode* Parent;

			//! List of all children of this node
			std::list<ISceneNode*> Children;

			//! List of all animator nodes
			std::list<ISceneNodeAnimator*> Animators;

			//! List of all animator nodes
			//std::list<ISceneNodeAnimator*> Animators;

			//! Pointer to the scene manager
			CSceneManager* SceneManager;

			//! Pointer to the triangle selector
			//ITriangleSelector* TriangleSelector;

			//! ID of the node.
			int ID;

			//! Is the node visible?
			bool IsVisible;


		};
	}
}
#endif//! __LDX_ISCENENODE_H_INCLUDE__